#include "DmMainAfx.h"
#include "BitBltImpl.h"

BOOL xRender::BitBlt(PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
    LPBYTE lpScanSrc = (LPBYTE)lpbSrc->bmBits;
    LPBYTE lpScanDst = (LPBYTE)lpbDst->bmBits;

    for (int j = lpbDst->bmHeight; j > 0; j--)
    {
        memcpy(lpScanDst, lpScanSrc, lpbDst->bmWidth * 4);
        lpScanSrc += lpbSrc->bmWidthBytes;
        lpScanDst += lpbDst->bmWidthBytes;
    }

    return TRUE;
}

BOOL xRender::BitBltSSE(PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
    LPBYTE lpScanSrc = (LPBYTE)lpbSrc->bmBits;
    LPBYTE lpScanDst = (LPBYTE)lpbDst->bmBits;
    DWORD dstWidth = lpbDst->bmWidth;
    for (int j = lpbDst->bmHeight; j > 0; j--)
    {
        __asm
        {
            mov esi, lpScanSrc
            mov edi, lpScanDst
            mov ecx, dstWidth
            mov eax, ecx
            and ecx, (not 7)
            je ENDLOOP
            lea esi, [esi+ecx*4]
            lea edi, [edi+ecx*4]
            neg ecx
WRITELOOP:
            prefetchnta [esi+ecx*4+512]
            movq mm0, [esi+ecx*4]
            movq mm1, [esi+ecx*4+8]
            movq mm2, [esi+ecx*4+16]
            movq mm3, [esi+ecx*4+24]
            movntq qword ptr [edi+ecx*4], mm0
            movntq qword ptr [edi+ecx*4+8], mm1
            movntq qword ptr [edi+ecx*4+16], mm2
            movntq qword ptr [edi+ecx*4+24], mm3
            add ecx, 8
            jnz WRITELOOP
ENDLOOP:
            mov ecx, eax
            and ecx, 7
            rep movsd
        }

        lpScanSrc += lpbSrc->bmWidthBytes;
        lpScanDst += lpbDst->bmWidthBytes;
    }
    __asm
    {
        sfence
        emms
    }
    return TRUE;
}

BOOL xRender::BitBltSSE2(PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
    LPBYTE lpScanSrc = (LPBYTE)lpbSrc->bmBits;
    LPBYTE lpScanDst = (LPBYTE)lpbDst->bmBits;
    DWORD dstWidth = lpbDst->bmWidth;
    for (int j = lpbDst->bmHeight; j > 0; j--)
    {
        __asm
        {
            mov esi, lpScanSrc
            mov edi, lpScanDst
            mov ecx, dstWidth
            mov eax, ecx
            and ecx, (not 15)
            je ENDLOOP
            lea esi, [esi+ecx*4]
            lea edi, [edi+ecx*4]
            neg ecx
WRITELOOP:
            prefetchnta [esi+ecx*4+512]
            movdqu xmm0, [esi+ecx*4]
            movdqu xmm1, [esi+ecx*4+16]
            movdqu xmm2, [esi+ecx*4+32]
            movdqu xmm3, [esi+ecx*4+48]
            movdqu [edi+ecx*4], xmm0
            movdqu [edi+ecx*4+16], xmm1
            movdqu [edi+ecx*4+32], xmm2
            movdqu [edi+ecx*4+48], xmm3 
            add ecx, 16
            jnz WRITELOOP
ENDLOOP:
            mov ecx, eax
            and ecx, 15
            rep movsd
        }

        lpScanSrc += lpbSrc->bmWidthBytes;
        lpScanDst += lpbDst->bmWidthBytes;
    }
    return TRUE;
}

BOOL xRender::BitBlt(PBITMAP lpbDst, PBITMAPEX lpbSrc, xARGB xKey)
{
    DWORD dwKey = xKey.argb;

    LPBYTE lpScanSrc = (LPBYTE)lpbSrc->bmBits;
    LPBYTE lpScanDst = (LPBYTE)lpbDst->bmBits;

    for (int j = lpbDst->bmHeight; j > 0; j--)
    {
        LPDWORD lpPixelSrc = (LPDWORD)lpScanSrc;
        LPDWORD lpPixelDst = (LPDWORD)lpScanDst;

        for (int i = lpbDst->bmWidth; i > 0; i--)
        {
            DWORD c = *lpPixelSrc;
            if (c != dwKey)
            {
                *lpPixelDst = c;
            }

            lpPixelSrc++;
            lpPixelDst++;
        }

        lpScanSrc += lpbSrc->bmWidthBytes;
        lpScanDst += lpbDst->bmWidthBytes;
    }

    return TRUE;
}

BOOL xRender::BitBltSSE(PBITMAP lpbDst, PBITMAPEX lpbSrc, xARGB xKey)
{
    LPBYTE lpScanSrc = (LPBYTE)lpbSrc->bmBits;
    LPBYTE lpScanDst = (LPBYTE)lpbDst->bmBits;
    DWORD dstWidth = lpbDst->bmWidth;
    __asm
    {
        movd mm2, xKey.argb
        punpckldq mm2, mm2
        movq mm6, mm2
    }
    for (int j = lpbDst->bmHeight; j > 0; j--)
    {
        __asm
        {
            mov esi, lpScanSrc
            mov edi, lpScanDst
            mov ecx, dstWidth
            mov eax, ecx
            and ecx, (not 3)
            je ENDLOOP
            lea esi, [esi+ecx*4]
            lea edi, [edi+ecx*4]
            neg ecx
WRITELOOP:
            prefetchnta [esi+ecx*4+512]
            movq mm0, [esi+ecx*4]
            movq mm4, [esi+ecx*4+8]
            movq mm1, [edi+ecx*4]
            movq mm5, [edi+ecx*4+8]
            movq mm3, mm0
            movq mm7, mm4
            pcmpeqd mm0, mm2
            pcmpeqd mm4, mm6
            psubusb mm3, mm0
            psubusb mm7, mm4
            pand mm0, mm1
            pand mm4, mm5
            por mm0, mm3
            por mm4, mm7
            movq qword ptr [edi+ecx*4], mm0
            movq qword ptr [edi+ecx*4+8], mm4
            add ecx, 4
            jnz WRITELOOP
ENDLOOP:
            and eax, 3
            je END
BACKLOOP:
            movd mm0, [esi+eax*4-4]
            movd mm1, [edi+eax*4-4]
            movq mm3, mm0
            pcmpeqd mm0, mm2
            psubusb mm3, mm0
            pand mm0, mm1
            por mm0, mm3
            movd [edi+eax*4-4], mm0
            dec eax
            jnz BACKLOOP
END:
        }

        lpScanSrc += lpbSrc->bmWidthBytes;
        lpScanDst += lpbDst->bmWidthBytes;
    }
    __asm
    {
        emms
    }
    return TRUE;
}